package framework
{
    import flash.utils.Proxy;
    import flash.utils.flash_proxy;

    import mx.collections.ArrayCollection;
    import mx.collections.IList;
    import mx.rpc.AsyncToken;
    import mx.rpc.events.ResultEvent;

    public dynamic class CachedService extends Proxy
    {

        public function CachedService(service:Object = null)
        {
            this.service = service;
        }

        public var service:Object;

        private var cache:Object = {};

        private var funcs:Object = {};

        private var funcsArgs:Object = {};

        public function refresh():void
        {
            for (var s:String in cache)
            {
                executeFunc(s, funcs[s], funcsArgs[s]);
            }
        }

        public function removeAll():void
        {
            for each (var s:String in cache)
            {
                ArrayCollection(cache[s]).removeAll();
            }
            cache = {};
        }

        public function addToCollection(addToken:AsyncToken, collection:IList):AsyncToken
        {
            handle(addToken, addFuncHandler);
            addToken.collection = collection;
            return addToken;
        }

        override flash_proxy function callProperty(name:*, ... args:Array):*
        {
            var s:String = makeCollectionName(name, args);
            var r:ArrayCollection = cache[s];
            if (!r)
            {
                r = new ArrayCollection;
                cache[s] = r;
                var f:Function = service[name] as Function;
                funcs[s] = f;
                funcsArgs[s] = args;
                executeFunc(s, f, args);
            }
            return r;
        }

        private function addFuncHandler(re:ResultEvent):void
        {
            var collection:IList = re.token.collection;
            if (collection)
                collection.addItem(re.token.result);
        }

        private function executeFunc(s:String, func:Function, args:Array):void
        {
            var a:AsyncToken = func.apply(this, args);
            handle(a, serviceCallHandler);
            a.collectionName = s;
        }

        private function makeCollectionName(name:*, args:Array):String
        {
            var s:String = String(name);
            for each (var obj:Object in args)
            {
                if ("id" in args)
                {
                    s += "-" + obj.id;
                }
                else
                {
                    s += "-" + obj;
                }
            }
            return s;
        }

        private function serviceCallHandler(re:ResultEvent):void
        {
            var s:String = re.token.collectionName;
            var r:ArrayCollection = cache[s];
            r.removeAll();
            r.addAll(IList(re.result));
        }
    }
}